// 获取Canvas元素,Canvas是HTML5中用于绘制图形的元素
const canvas = document.getElementById('gameCanvas')
//获取2D渲染上下文,这是在Canvas上绘制图形所需的工具
const ctx = canvas.getContext('2d')
//设置canvas的宽度和宽度,Flappy Bird经典尺寸
canvas.width = 288
canvas.height = 512

//加载图片资源
//imagas 独享用于存储加载完成的Image对象
const images = {}
//imageSources对象存储所有需要加载图片的路径,键是图片在游戏中的名称,值是图片文件路径
const imagesSources = {
  bird0: 'imgs/0.png', //鸟的第0帧动画
  bird1: 'imgs/1.png', //鸟的第1帧动画
  bird2: 'imgs/2.png', //鸟的第2帧动画
  bird3: 'imgs/3.png', //鸟的第3帧动画
  bird4: 'imgs/4.png', //鸟的第4帧动画
  bird5: 'imgs/5.png', //鸟的第5帧动画
  bird6: 'imgs/6.png', //鸟的第6帧动画
  bird7: 'imgs/7.png', //鸟的第7帧动画
  bg: 'imgs/bg.png', //背景
  gameover: 'imgs/gameover.png', //游戏结束
  pipebottom: 'imgs/pipebottom.png', //下部管道
  pipetop: 'imgs/pipetop.png', //上部管道
  start: 'imgs/start.png', //开始界面
  ground: 'imgs/ground.png', //地面
}

//imagesLoaded记录已加载图片的数量
let imagesLoaded = 0
//totalImages 记录总共需要加载的图片数量
const totalImages = Object.keys(imagesSources).length

//LoadImages函数用于加载所有图片资源
//它接受一个回调函数作为参数,在所有图片加载完成后执行
function loadImages(callback) {
  //遍历imagesSource对象
  for (let key in imagesSources) {
    //创建一个新的Image对象
    images[key] = new Image()
    //设置图片的onload事件处理函数,当图片加载成功后触发
    images[key].onload = function () {
      imagesLoaded++
      if (imagesLoaded >= totalImages) {
        callback()
      }
    }
    images[key].src = imagesSources[key]
  }
}

//游戏状态
//'start':游戏开始界面
//'playing':游戏进行中
//'gameover':游戏结束界面
let gameState = 'start'

//游戏常量
const PIPE_SPEED = 2 //管道向左移动速度
const PIPE_SPAWN_INTERVAL = 90 //生成一对新管道的帧间隔
const PIPE_GAP = 200 //上下管道之间的垂直间隙大小
const MIN_PIPE_HEIGHT = 50 //管道的最小高度

//游戏变量
let bird = {
  x: 50, //鸟的x坐标
  y: 200, //鸟的y坐标
  width: 34,
  height: 24,
  gravity: 0.25, //重力加速度
  velocity: 0, //鸟当前的垂直速度
  jump: -4.6, //鸟跳跃时的垂直速度
  animationFrame: 0, //当前鸟的动画帧索引
  animationSpeed: 5, //控制鸟动画速度,值越大动画越慢
}

//pipes数组存储当前屏幕上的所有管道对象
let pipes = []
//score记录玩家得分
let score = 0
//frame记录游戏循环执行的帧数,用于控制管道生成和鸟动画
let frame = 0

//游戏循环函数,使用requestAnimationFrame来创建流畅的动画
function gameLoop() {
  //清空画布,为下一帧绘制做准备
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  //绘制背景图片,覆盖整个canvas
  ctx.drawImage(images.bg, 0, 0, canvas.width, canvas.height)
  //更具当前游戏状态执行不同的逻辑和绘制
  if (gameState === 'start') {
    //如果是开始界面,绘制开始图片
    ctx.drawImage(
      images.start,
      canvas.width / 2 - images.start.width / 2, //计算图片居中的x坐标
      canvas.height / 2 - images.start.height / 2 //计算图片居中的y坐标
    )
  } else if (gameState === 'playing') {
    //如果游戏进行中
    //更新鸟的位置和速度;速度受重力影响,位置受速度影响
    bird.velocity += bird.gravity
    bird.y += bird.velocity

    //限制鸟不掉出顶部边界
    if (bird.y < 0) {
      bird.y = 0
      bird.velocity = 0 //碰到顶部速度归零
    }

    //绘制鸟,根据帧数切换鸟的动画图片
    //Math.floor(frame/bird.animationSpeed)控制动画速度
    //%8使动画在bird0到bird7之间循环
    const birdImage =
      images['bird' + (Math.floor(frame / bird.animationSpeed) % 8)]
    ctx.drawImage(birdImage, bird.x, bird.y, bird.width, bird.height)
    //生成管道:每隔PIPE_SPAWN_INTERVAL帧生成一对新的管道
    if (frame % PIPE_SPAWN_INTERVAL === 0) {
      //计算上下管道之间的间隙高度和位置

      //maxPipeHeight 计算上方管道的最大可能高度,确保下方管道有足够空间且地面可见
      const maxPipeHeight =
        canvas.height - images.ground.height - PIPE_GAP - MIN_PIPE_HEIGHT
      //随机生成上方管道的高度
      const topPipeHeight =
        Math.floor(Math.random() * maxPipeHeight) + MIN_PIPE_HEIGHT
      //计算生成下方管道的高度
      const bottomPipeHeight =
        canvas.height - PIPE_GAP - topPipeHeight - images.ground.height

      //将新的管道对象添加到pipes数组中
      pipes.push({
        x: canvas.width, //管道的起始x坐标(画布右侧)
        y: canvas.height - images.ground.height - bottomPipeHeight, //管道的起始x坐标(画布右侧)
        topHeight: topPipeHeight, //上方管道的高度
        bottomHeight: bottomPipeHeight, //下方管道的高度
        width: 52, //管道的宽度
        scored: false, //标记这对管道是否已经让玩家得分
      })
    }

    //移动和绘制管道
    //遍历pipes数组,处理每一对数组
    for (let i = 0; i < pipes.length; i++) {
      const pipe = pipes[i]
      //管道向左移动,速度为PIPE_SPEED 像素/帧
      pipe.x -= PIPE_SPEED

      //绘制上方管道
      ctx.drawImage(images.pipetop, pipe.x, 0, pipe.width, pipe.topHeight)
      //绘制下方管道
      ctx.drawImage(
        images.pipebottom,
        pipe.x,
        pipe.y,
        pipe.width,
        pipe.bottomHeight
      )

      //移除移出屏幕左侧的管道,优化性能
      if (pipe.x + pipe.width < 0) {
        pipes.splice(i, 1) //移除当前管道对象
        i-- //因为移除了一个元素,需要调整索引以避免跳过下一个管道
      }

      //检查鸟是否通过管道(得分)
      //当鸟的右侧超过管道的右侧,并且还没有得分时,分数甲乙
      if (!pipe.scored && bird.x > pipe.x + pipe.width) {
        score++
        pipe.scored = true
      }

      //碰撞检测(地面与管道)
      if (
        bird.y + bird.height > canvas.height - images.ground.height || //撞击地面
        (bird.x + bird.width > pipe.x && //鸟右在管左的右边
          bird.x < pipe.x + pipe.width && //鸟左在管左的左边
          (bird.y < pipe.topHeight || //当鸟上在上管的上边
            bird.y + bird.height > pipe.y)) //当鸟下在下管的下边
      ) {
        gameState = 'gameover' //状态设为gameover
      }
    }
  } else if ((gameState = 'gameover')) {
    ctx.drawImage(
      images.gameover,
      canvas.width / 2 - images.gameover.width / 2,
      canvas.height / 2 - images.gameover.height / 2
    )
  }

  //绘制地面 位于canvas底部
  ctx.drawImage(
    images.ground,
    0,
    canvas.height - images.ground.height,
    canvas.width,
    images.ground.height
  )

  //绘制分数
  ctx.fillStyle = 'white' //设置文字颜色为白色
  ctx.font = '20px Arial' //设置文字字体和大小
  ctx.fillText('Score: ' + score, 10, 30) //在指定位置绘制分数文本

  //请求下一帧动画,实现游戏循环
  requestAnimationFrame(gameLoop)
  //帧数加一
  frame++
}

//监听Canvas的点击事件,用于控制游戏状态和鸟的跳跃
canvas.addEventListener('click', function () {
  //如果当前是开始界面
  if (gameState === 'start') {
    //状态设为playing (gameLoop()函数自动执行 if (gameState === 'playing') {playing事件(渲染playing图片...)})
    //切换到游戏进行中状态,开始游戏
    gameState = 'playing'
    //重置游戏变量到初始状态
    bird.y = 200 //鸟回到初始垂直位置
    bird.velocity = 0 //鸟当前的垂直速度重置为0
    pipes = [] //清空管道数组
    score = 0 //分数清零
    frame = 0 //帧数归零
  } else if (gameState === 'playing') {
    bird.velocity = bird.jump
  } else if (gameState === 'gameover') {
    gameState = 'start' //状态设为开始(gameLoop()函数自动执行 if (gameState === 'start') {start事件(渲染start图片...)})
  }
})

//加载图片并开始游戏循环
//loadImages函数加载完所有图片后,会执行传入的回调函数,即开始游戏循环
loadImages(function () {
  console.log('All images loaded successfully')
  gameLoop()
})
